/**
 * 通过Object.defineProperty() 方法给car对象添加了price属性，并被这个price的读取和写入分别用get()和set()去拦截
 * 每当price 属性执行读和写的操作的时候就会触发get()和set() 方法
 * 
*/

// let car = {};
// let price = 3000;
// Object.defineProperty(car,'price',{
//     enumerable:true,
//     configurable:true,
//     get(){
//         console.log(`price属性被读取了`);
//         return val
//     },
//     set(newVal){
//         console.log(`price属性被修改了-> ${newVal}`);
//         val = newVal
//     },
// });
// car.price;

/*   car对象现在已经可以主动告诉我读写情况了，这也意味着，这个car的数据对象已经是可以观测的了  */

/*   为了把car的所有属性都变的可以观测，我们编写代码如下  */

//  源码位置 src/core/observer/index.js

/**
 * Observer类会通过递归的方式把一个对象的属性都转换了可以观测的
 * Observer类 位于源码src/core/observer/index.js
 * 
*/
class Observer{
    constructor(value){
        this.value = value;
        if(Array.isArray(value)){
            // 当value为数组时的逻辑 ...
        }else{
            this.walk(value)
        }
    }

    walk(obj){
        const keys = Object.keys(obj);
        for (let i = 0; i < keys.length; i++) {
            defineReactive(obj,keys[i]);
        }
    }
}
    /**
     * name @defineReactive
     * describe @使一个对象转换为可疑观测的对象
     * @params { Object } obj 对象
     * @params { String } key 对象的key
     * @params { Any } val对象的摸一个key值
     * 
    */
function defineReactive(obj, key, val) {
    // 如果只传了 obj和key,那么val = obj['key']
    if (arguments.length === 2){
        val = obj[key]
    }
    // 如果传入的val 是一个object类型
    if(typeof val === 'object'){
        new Observer(val)
    }
    let dep = new Dep()
    Object.defineProperty(obj, key, {
        enumerable:true,
        configurable:true,
        get(){
            console.log(`${key}属性被读取了`);
            // 谁 用到了数据，就是谁获取了这个数据，而可观测的数据被获取时会触发getter属性
            // 那么我们就可以在getter 中手机依赖同样，当这个数据变化时会触发setter属性，那么我们就可以在setter中通知依赖更新。
            dep.depend() 
            return val; 
        },
        set(newVal){
            if(val === newVal){
                return
            }
            console.log(`${key}属性被修改了`)
            val = newVal
            dep.notify()// 在setter中通知依赖更新
        }
    })
} 

// let obj = {
//     age:19,
//     name:'huizi'
// }
// new Observer(obj);
// obj.name = '辉子';
// obj.age

/**
 * 上一步通过observer类。让object类型的数据变换的可以观测;变得可以观测后,我们就能知道数据什么时候发生变化,那么当数据发生变化时,我们去通知视图更新就好了。
 * 那么问题又来了，视图那么大，我们到底该通知谁去变化？总不能一个数据变化了，把整个视图全部更新一遍吧，这样显然是不合理的。
 * 此时，你肯定会想到，视图里谁用到了这个数据就更新谁呗。对！你想的没错，就是这样。
 * 
 * 视图里谁用到了这个数据就更新谁，我们换个优雅说法：我们把"谁用到了这个数据"称为"谁依赖了这个数据",我们给每个数据都建一个依赖数组(因为一个数据可能被多处使用)，
 * 谁依赖了这个数据(即谁用到了这个数据)我们就把谁放入这个依赖数组中，那么当这个数据发生变化的时候，我们就去它对应的依赖数组中，把每个依赖都通知一遍，
 * 告诉他们："你们依赖的数据变啦，你们该更新啦！"。这个过程就是依赖收集.
 * 
 * 总结一句话就是：在getter中收集依赖，在setter中通知依赖更新。
*/


/**
 * 把依赖收集到哪里？(Dep依赖收集类)
 * 明白了什么是依赖收集以及何时收集何时通知后，那么我们该把依赖收集到哪里？
 * 我们给每个数据都建一个依赖数组，谁依赖了这个数据我们就把谁放入这个依赖数组中。
 * 单单用一个数组来存放依赖的话，功能好像有点欠缺并且代码过于耦合。
 * 我们应该将依赖数组的功能扩展一下，更好的做法是我们应该为每一个数据都建立一个依赖管理器，把这个数据所有的依赖都管理起来。
 * OK，到这里，我们的依赖管理器Dep类应运而生，代码如下：
 * 
 * // 源码位置：src/core/observer/dep.js
*/
class Dep{
    constructor(){
        this.subs = []
    }
    // 增加一个依赖
    addSub(){
        this.subs.push(sub);
    }
    // 删除一个依赖
    removeSub(sub){
        remove(this.subs,sub)
    }
    //增加一个依赖
    depend(){
        if (window.target) {
            this.addSub(window.target)
        }
    }
    // 通知所有依赖更新
    notify(){
        const subs = this.subs.slice()
        for (let i = 0, l = subs.length; i < l.length; i++) {
            subs[i] = updata()
        }
    }
}

/**
 * Remove an item from an array
*/
function remove(arr,item){
    for (let i = 0; i < arr.length; i++) {
        if(arr.length){
            const index = arr.indexOf(item)
            if (index > -1) {
                return arr.splice(index, 1);
            }
        }
    }
}